#include <iostream>
#include <vector>
#include <memory>
#include <mutex>
#include <thread>
#include <algorithm> // Include this for std::remove_if
class Observer {
public:
    virtual ~Observer() {}
    virtual void update(int message) = 0;
};

class Subject {
private:
    std::vector<std::weak_ptr<Observer>> observers;
    std::mutex mutex;

public:
    void registerObserver(std::shared_ptr<Observer> observer) {
        std::lock_guard<std::mutex> lock(mutex);
        observers.push_back(observer);
        std::cout << "Observer registered.\n";
    }

    void unregisterObserver(std::shared_ptr<Observer> observer) {
        std::lock_guard<std::mutex> lock(mutex);
        observers.erase(
            std::remove_if(
                observers.begin(), observers.end(),
                [&observer](const std::weak_ptr<Observer>& weak_obs) {
                    return (weak_obs.expired() || weak_obs.lock() == observer);
                }),
            observers.end());
        std::cout << "Observer unregistered.\n";
    }

    void notifyObservers(int message) {
        std::lock_guard<std::mutex> lock(mutex);
        for (auto weak_obs : observers) {
            auto obs = weak_obs.lock();
            if (obs) {
                obs->update(message);
                std::cout << "Observer notified.\n";
            }
        }
    }
};

class ConcreteObserver : public Observer, public std::enable_shared_from_this<Observer> {
public:
    void update(int message) override {
        std::cout << "Received message: " << message << std::endl;
    }
};

int main() {
    std::shared_ptr<Subject> subject = std::make_shared<Subject>();
    std::shared_ptr<Observer> observer1 = std::make_shared<ConcreteObserver>();
    std::shared_ptr<Observer> observer2 = std::make_shared<ConcreteObserver>();

    subject->registerObserver(observer1);
    subject->registerObserver(observer2);

    subject->notifyObservers(1);

    subject->unregisterObserver(observer1);
    subject->notifyObservers(2);

    return 0;
}